#include <iostream>
#include <fstream>
#include <sstream>

// io流状态
// io流在读/写的过程中，可能发生任何错误, IO流对象给了四种状态标识错误
// goodbit eofbit failbit badbit
// goodbit 表示IO流正常，没有任何错误
// eofbit  表示遇到文件末尾
// failbit 逻辑错误，比如要读一个int，你却输入了一个char，此时cin对象的failbit就为true
// badbit  表示遇到系统级错误, 一旦badbit别设置了，流就无法使用了

int main1()
{
    std::cout << std::cin.good() << std::endl;
    std::cout << std::cin.eof() << std::endl;
    std::cout << std::cin.fail() << std::endl;
    std::cout << std::cin.bad() << std::endl << std::endl;

    int i = 0;
    std::cin >> i;  // 输入字符，failbit被设置，无法使用，除非使用clear重置
    if(std::cin.fail()) {
        std::cin.clear();
        std::cout << std::cin.good() << std::endl;
        std::cout << std::cin.eof() << std::endl;
        std::cout << std::cin.fail() << std::endl;
        std::cout << std::cin.bad() << std::endl << std::endl;
        // 将字符读走
        char ch = std::cin.peek();
        while(!(ch >= '0' && ch <= '9')) {
            ch = std::cin.get(); // 读一个字符
            std::cout << ch;
            ch = std::cin.peek();
        }
        std::cout << "\n";
    }
    std::cout << std::cin.good() << std::endl;
    std::cout << std::cin.eof() << std::endl;
    std::cout << std::cin.fail() << std::endl;
    std::cout << std::cin.bad() << std::endl << std::endl;

    std::cin >> i; // 这里还不能是因为那个X没有被读走，重置之后发现又是x

    std::cout << std::cin.good() << std::endl;
    std::cout << std::cin.eof() << std::endl;
    std::cout << std::cin.fail() << std::endl;
    std::cout << std::cin.bad() << std::endl << std::endl;

    // 2.当读到文件末尾时，eofbit和failbit都会被设置，此时IO流无法使用
    // 如果想要继续使用使用clear()重置状态为goodbit

    return 0;
}


// IO缓冲区机制
// 不管是输出到控制台，还是输出到文件中，我们在进行IO的都是都在访问外设
// 而操作系统与外设的访问是比较慢的，如果经常性的访问外设，就会导致我们的IO效率特别低
// 所以为了避免这个问题，我们的做法就是减少与外设的IO次数，那么就需要我们将很多数据暂存起来，然后一次性刷新到外设上
// 而这就是我们的刷新策略
// 刷新策略有很多种，行刷新/全刷新等等

// 而为了将数据暂存起来，就得有一个缓冲区。任何IO流都有缓冲区。
// 1.程序正常结束，会刷新缓冲区
// 2.缓冲区满了，会刷新缓冲区
// 3.使用了endl/flush会刷新缓冲区， endl和flush都是函数，endl除了为打印一个换行外，还会调用flush刷新缓冲区
// 4.设置了unitbuf，就会立刻刷新缓冲区，cerr就设置了unitbuff，nounitbuf，取消立刻刷新
// 5.一个流关联了另一个流时，一个流读写时，另一个流就会刷新, cin和cout关联了， 使用tie关联两个流，取消关联，参数传nullptr

int main2() {
    // 1. 使用endl：输出换行符并刷新缓冲区
    std::cout << "Hello, World!" << std::endl;  // 等价于 cout << "Hello, World!\n" << flush;

    // 2. 使用flush：仅刷新缓冲区，不输出换行
    std::cout << "Loading..." << std::flush;

    // 3. 使用unitbuf：设置流为无缓冲模式（每次操作后自动刷新）
    std::cout << std::unitbuf;
    std::cout << "Step 1" << "Step 2";  // 每次输出后立即刷新
    std::cout << std::nounitbuf;  // 恢复默认缓冲模式

    // 4. 对文件流手动刷新
    std::ofstream file("data.txt");
    file << "some data";
    file.flush();  // 立即将缓冲区数据写入文件

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////
// 文件IO流
int main3()
{
    std::ofstream ofs("data.txt"); //默认打开方式 std::ios::out
    ofs << "aaaaabbbbbbbbbbb\n";
    ofs.close(); // 关闭文件，也可以不关闭，ofs对象析构时，会自动关闭文件

    std::ofstream ofs1("data.txt", std::ios::app); //追加方式打开
    ofs1 << "1234556\n";
    ofs1.close();
    return 0;
}

// fstream继承于iostream，所以也有good/fail/eof/bad的接口，用来判断IO是否正常
int main4()
{
    // 2进制读写
    std::ifstream ifs("/home/xsc/图片/IMG_2704.JPG", std::ios::binary);
    std::ofstream ofs("copy.JPG", std::ios::binary);
    int n = 0;
    while(ifs && ofs) {
        char ch = ifs.get();
        ofs << ch;
        ++n;
    }
    std::cout << n << std::endl;

    std::cout << ifs.good() << std::endl; // 0
    std::cout << ifs.eof() << std::endl;  // 1
    std::cout << ifs.fail() << std::endl; // 1
    std::cout << ifs.bad() << std::endl << std::endl; // 0
    return 0;
}

// stringstream
// 对字符串操作的流
class Date
{
    friend std::ostream& operator << (std::ostream& out, const Date& d);
    friend std::istream& operator >> (std::istream& in, Date& d);
public:
    Date(int x, int y, int z) :_year(x), _month(y), _day(z){}
private:
    int _year;
    int _month;
    int _day;
};
std::istream& operator >> (std::istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

std::ostream& operator << (std::ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day << std::endl;
	return out;
}

int main()
{
    int a = 1;
    double b = 32.1;
    Date d = {2001, 1, 1};

    std::stringstream ss;
    ss << a << " " << b << " ";
    ss << d;

    std::cout << ss.str();

    int a1 = 0;
    double b1 = 0;
    Date d1(0, 0, 0);
    ss >> a1 >> b1 >> d1;
    std::cout << "a1->" << a1 << " ";
    std::cout << "b1->" << b1 << " ";
    std::cout << "d1->" << d1 << " ";
    return 0;
}